#include "..\interface_types.h"
#include "d3d_lightshader.h"
#include "d3d_depthmapshader.h"
#include "..\d3d_texture.h"
#include "..\d3d_light.h"
#include "..\d3d.h"
#include "..\d3d_util.h"
#include "..\d3d_mesh.h"
#include "..\..\common\string.h"
#include "..\..\common\def.h"
#include "..\..\engine\engine.h"
//#include "..\..\engine\memorymanager.h"
#include "..\..\engine\error.h"

D3DLightShader::D3DLightShader(CD3D9Device *pDevice) : D3DShader(pDevice)
{
//m_QualityDenominator = 2;
	mp_LightDataHandle = NULL; 

	mp_ComputeDirLightingHandle = NULL; 
	mp_MaskDepthNormalHandle = NULL; 
	mp_ComputeLightingHandle = NULL; 
	mp_DepthNormalHandle = NULL; 
	mp_LastViewHandle = NULL; 
	m_pInvMapDimensions	= NULL;
	mp_MaskInstanceVB = NULL; 
	mb_Locked = false; 

	m_pLightSphere = NULL;
	m_pLightCylinder = NULL;

	m_RTDepth = NULL; 
	m_RTNormal= NULL; 
	m_RTVelocity = NULL; 
	m_RTLightbuffer= NULL; 
	m_RTStaticLighting = NULL;

	matLastView = CD3D9Device::m_IdentityMatrix;
	pLightTextureA = NULL; 
}

string D3DLightShader::Identifier()
{
	return "D3DLightShader"; 
}
string D3DLightShader::GetType() const
{
	return string("D3DLightShader"); 
}

/**************************************
 * Allows client to submit geometry
 * that will have this shader be applied
 * to it during rendering
 **************************************/
void D3DLightShader::AddRenderQue(D3DRenderable* pMesh, const Matrix4f& transform)
{
	Vector3f velocity = pMesh->getVelocity();
	InstanceData instance; 
	instance.fData[0][0] = transform._M11; 
	instance.fData[0][1] = transform._M12; 
	instance.fData[0][2] = transform._M13; 
	instance.fData[0][3] = velocity.x; 

	instance.fData[1][0] = transform._M21; 
	instance.fData[1][1] = transform._M22; 
	instance.fData[1][2] = transform._M23; 
	instance.fData[1][3] = velocity.y; 

	instance.fData[2][0] = transform._M31; 
	instance.fData[2][1] = transform._M32; 
	instance.fData[2][2] = transform._M33; 
	instance.fData[2][3] = velocity.z; 

	instance.fData[3][0] = transform._M41; 
	instance.fData[3][1] = transform._M42; 
	instance.fData[3][2] = transform._M43; 
	instance.fData[3][3] = 1.0f; 

	if(pMesh->getMaskTexture())
		m_MaskMeshHashInstances.Add(pMesh, instance); 
	else
		m_MeshHashInstances.Add(pMesh, instance); 
	
	m_SubmittedVertexCount += pMesh->getVertexCount();
}

/**************************************
 * Abstracts the task of instance buffer 
 * creation
 **************************************/
HRESULT D3DLightShader::ConstructSubGroupInstanceBuffer(HashTable<InstanceData, D3DRenderable*>* pSubGroup, unsigned int* pLastInstanceCount, LPDIRECT3DVERTEXBUFFER9* pInstanceBuffer)
{
#ifdef _DEBUG
	if(ISNULL(pSubGroup) || ISNULL(pLastInstanceCount))
	{
		EE_LOGERROR("Bad parameters.");
		pSubGroup->Empty();
		*pInstanceBuffer = NULL;
		return E_FAIL;
	}
#endif

	LPDIRECT3DVERTEXBUFFER9 pIB = *pInstanceBuffer;
	unsigned int meshCount = 0; 
	unsigned int totalCount = 0; 
	pSubGroup->GetDataInfo(&meshCount, &totalCount); 

	if(meshCount == 0)
	{
		pSubGroup->ClearAllocatedSpace();
		*pInstanceBuffer = NULL;
		return E_FAIL;
	}

	D3DShader::InstanceData *pTmpInstTrans = NULL; 
	LPDIRECT3DDEVICE9 pDevice = GetDevice()->GetDevice(); 

	//Create instancing buffer for non-masked meshes
	if(totalCount > *pLastInstanceCount || !pIB)
	{
		*pLastInstanceCount = totalCount; 
		if(pIB) 
		{
			pIB->Release(); 
			pIB = NULL; 
		}
		if(pDevice->CreateVertexBuffer(totalCount * sizeof(D3DShader::InstanceData),0,0,
			D3DPOOL_DEFAULT,&pIB,NULL) != D3D_OK)
		{
			pSubGroup->Empty();
			*pInstanceBuffer = NULL;
			return E_FAIL;
		}
	}

	pTmpInstTrans = NULL; 
	if(SUCCEEDED(pIB->Lock(0, NULL, (void **) &pTmpInstTrans, 0)))
	{
		InstanceData* pMeshBaseByte = pTmpInstTrans; 
		for(unsigned int i = 0;  i < meshCount;  i++)
		{
			HashTable<InstanceData, D3DRenderable*>::Pair *pPair = pSubGroup->GetPair(i); 		
			memcpy(pMeshBaseByte, pPair->m_pData, pPair->m_Count * sizeof(D3DShader::InstanceData)); 
			pMeshBaseByte += pPair->m_Count; 
		}

		pIB->Unlock(); 
	}

	*pInstanceBuffer = pIB;
	return S_OK;
}

/**************************************
 * Constructs an instance buffer for this
 * shader based on the passed mesh instances
 * in the last frame
 **************************************/
HRESULT D3DLightShader::ConstructInstanceBuffer()
{
	unsigned int vbSize = m_SubmittedVertexCount * VertexComplex::size;
	if(m_pMeshVB) 
	{
		m_pMeshVB->Release(); 
		m_pMeshVB = NULL; 
	}

	LPDIRECT3DDEVICE9 pDevice = GetDevice()->GetDevice();
	//Build up VB of all meshes (they share the same vertex declaration)
	if(pDevice->CreateVertexBuffer(vbSize,0,0, D3DPOOL_DEFAULT,&m_pMeshVB,NULL) != D3D_OK)
		return E_FAIL; 

	VertexComplex *pTmpMeshVertex = NULL; 
	if(m_pMeshVB->Lock(0, NULL, (void **) &pTmpMeshVertex, 0) != D3D_OK)
		return E_FAIL;

	unsigned int meshCount;
	unsigned int i;
	m_MeshHashInstances.GetDataInfo(&meshCount, NULL);
	for(i = 0;  i < meshCount;  i++)
	{
		D3DRenderable*	pMesh = m_MeshHashInstances.KeyAtTableIndex(i);
		unsigned int	meshVertexCount = pMesh->getVertexCount();
		memcpy(pTmpMeshVertex, pMesh->getVertices(), meshVertexCount * pMesh->m_ElementSize); 

		//TODO:fix bug with single triangles
		/*if(meshVertexCount < 9)
		{
int k= 3;
k++;
		}*/
		pTmpMeshVertex += meshVertexCount; 
	}
	m_MaskMeshHashInstances.GetDataInfo(&meshCount, NULL);
	for(i = 0;  i < meshCount;  i++)
	{
		D3DRenderable*	pMesh = m_MaskMeshHashInstances.KeyAtTableIndex(i);
		unsigned int	meshVertexCount = pMesh->getVertexCount();
		memcpy(pTmpMeshVertex, pMesh->getVertices(), meshVertexCount * pMesh->m_ElementSize); 
		pTmpMeshVertex += meshVertexCount; 
	}

	m_pMeshVB->Unlock(); 

	if(FAILED(ConstructSubGroupInstanceBuffer(&m_MeshHashInstances, &m_lastInstanceCount, &m_pInstanceVB)))
		return E_FAIL;
	if(FAILED(ConstructSubGroupInstanceBuffer(&m_MaskMeshHashInstances, &mui_LastMaskInstanceCount, &mp_MaskInstanceVB)))
		return E_FAIL;

	return S_OK;
}

/**************************************
 * Appreciates that shaders can be executed
 * many times perframe. This provides opporutnity
 * to preform one-off initilization on a per-frame
 * basis. Blocks further shader operations for
 * this frame if function fails internally
 **************************************/
void D3DLightShader::PreExecute()
{
	if(FAILED(ConstructInstanceBuffer()))
		return;

	mb_Locked = true; 
}

/**************************************
 * Renders the omni light sources
 * into relevant buffers
 **************************************/
void D3DLightShader::RenderOminLighting()
{
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"Begin omni lights\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif 

	uint passes; 
	m_pEffect->SetTechnique(mp_ComputeLightingHandle); 
	
	for(unsigned int numLights = GetDevice()->m_Lights.Count(), L = 0;  L < numLights; L++)
	{
		D3DLight *pLight = GetDevice()->m_Lights[L];
		if(pLight && pLight->GetState() == D3DLight::LIGHT_ON) {
		
			m_pEffect->SetValue(mp_LightDataHandle, (void*)pLight->GetShaderData(), D3DLight::ShaderData::size); 
			m_pEffect->Begin(&passes, 0); 
			for(uint p = 0;  p < passes;  p++)
			{
				m_pEffect->BeginPass(p); 
				m_pLightSphere->DrawSubset(0);
				m_pEffect->EndPass(); 
			}
			m_pEffect->End(); 
		}
	}

#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"End omni lights\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif 
}

/**************************************
 * Renders the directional light sources
 * into relevant buffers
 **************************************/
void D3DLightShader::RenderDirectionalLighting()
{
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"Begin directional lights\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
#define SHADOW_MAP_UNIT 4

	uint passes;
	GetDevice()->GetDevice()->SetSamplerState(SHADOW_MAP_UNIT, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); 
	GetDevice()->GetDevice()->SetSamplerState(SHADOW_MAP_UNIT, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); 
	m_pEffect->SetTechnique(mp_ComputeDirLightingHandle); 	

	for(unsigned int numLights = GetDevice()->m_DirectionalLights.Count(), L = 0;  L < numLights;  L ++)
	{
		D3DDirectionalLight *pLight = GetDevice()->m_DirectionalLights[L];
		if(pLight && pLight->GetState() == D3DLight::LIGHT_ON) {

			m_pEffect->SetValue(mp_LightDataHandle, (void*)GetDevice()->m_DirectionalLights[L]->GetShaderData(), D3DLight::ShaderData::size); 
			GetDevice()->SetTexture(SHADOW_MAP_UNIT, pLight->IsShadowed() ? pLight->mDepthBuffer->m_pTargetTexture : GetDevice()->m_pWhiteTexture->DataD3D());

			m_pEffect->Begin(&passes, 0); 
			for(uint p = 0;  p < passes;  p++)
			{
				m_pEffect->BeginPass(p); 
				m_pLightCylinder->DrawSubset(0);
				m_pEffect->EndPass(); 
			}
			m_pEffect->End(); 
		}
	}

	GetDevice()->GetDevice()->SetSamplerState(SHADOW_MAP_UNIT, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
	GetDevice()->GetDevice()->SetSamplerState(SHADOW_MAP_UNIT, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 

#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"End directional lights\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
}

/**************************************
 * Renders the sun light if it is present
 **************************************/
void D3DLightShader::RenderSunLighting()
{
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"Begin sun light\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif

	if(m_skyActive) {

		m_pEffect->SetTechnique(mp_ComputeSunLightingHandle); 	
		m_pEffect->SetValue(mp_LightDataHandle, (void*)&m_skyData, D3DLight::ShaderData::size); 
		uint passes;

		m_pEffect->Begin(&passes, 0); 
		for(uint p = 0;  p < passes;  p++) {
			m_pEffect->BeginPass(p); 
			GetDevice()->RenderScreenGeometry();
			m_pEffect->EndPass(); 
		}

		m_pEffect->End(); 
	}
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"End sun light\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
}

/**************************************
 * Renders the lighting buffer from each
 * registered light source
 **************************************/
void D3DLightShader::RenderLighting()
{
	const LPDIRECT3DTEXTURE9 pDepthTexture = m_RTDepth->m_pTargetTexture; 
	const LPDIRECT3DTEXTURE9 pNormalTexture = m_RTNormal->m_pTargetTexture; 
	const LPDIRECT3DTEXTURE9 pStaticLightTexture = m_RTStaticLighting->m_pTargetTexture; 
	LPDIRECT3DDEVICE9 pDevice = GetDevice()->m_pD3DDevice; 
	
 
	GetDevice()->SetTexture(0,pDepthTexture); 
	GetDevice()->SetTexture(1,pNormalTexture);
	GetDevice()->SetTexture(2,pStaticLightTexture);
	
	
	pDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); 

	pDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_POINT); 

	pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);

	
	m_pEffect->SetVector(mp_CameraWSHandle, &VectorToD3DVector4(CEngine::GetInstance()->GetCamera()->getPosition()));
	
	pDevice->SetRenderTarget(0, m_RTLightbuffer->m_pSurface); 
	pDevice->Clear(0L, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L); 

	pDevice->SetRenderTarget(1, NULL); 
	//pDevice->SetRenderTarget(1, GetDevice()->m_pRTBloomBuffer->m_pSurface); 
	//pDevice->Clear(1, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1.0f, 0); 

	pDevice->BeginScene();
	RenderSunLighting();
	RenderDirectionalLighting();
	RenderOminLighting();
	pDevice->EndScene(); 
	
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"leave lights\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
	
	pDevice->SetRenderTarget(0, NULL); 
	pDevice->SetRenderTarget(1, NULL); 
	pLightTextureA = m_RTLightbuffer->m_pTargetTexture;

//pLightTextureA = !mb_SwapTarget ? m_RTLightbuffer->m_pTargetTexture : m_RTLightbuffer->m_pTargetTexture; 

	m_MaskMeshHashInstances.ClearAllocatedSpace(); 
	m_MeshHashInstances.ClearAllocatedSpace(); 

	
	pDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 
	pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); 

	pDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); 
	pDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 
	pDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); 
	pDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_POINT); 
}

/**************************************
 * Renders the normal/depth buffers for
 * reuse by the lighting equations
 **************************************/
void D3DLightShader::RenderNormalDepthBuffer()
{
	
#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"Render Normal/Depth\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
	LPDIRECT3DDEVICE9 pDevice = GetDevice()->m_pD3DDevice; 

	pDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
	pDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
	pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

	pDevice->SetRenderTarget(0, m_RTDepth->m_pSurface); 
	pDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffff0000, 1.0f, 0l); 
	pDevice->SetRenderTarget(1, m_RTNormal->m_pSurface); 
	pDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0); 
	/*pDevice->SetRenderTarget(2, m_RTVelocity->m_pSurface); 
	pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_COLORVALUE(0.5f,0.5f,0.0f,0.0f), 1.0f, 0);*/
	pDevice->SetRenderTarget(2, m_RTStaticLighting->m_pSurface); 
	pDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0);
	

	pDevice->BeginScene(); 

	pDevice->SetStreamSource(0, m_pMeshVB, 0, VertexComplex::size); 
	
	const unsigned int meshCount = m_MeshHashInstances.TableKeyCount(); 
	unsigned int uiInstanceBytesOffset = 0; 
	unsigned int vbBytesOffset = 0; 
	m_pEffect->SetTechnique(mp_DepthNormalHandle ); 
	for(unsigned int i = 0;  i < meshCount;  i++)
	{
		HashTable<InstanceData, D3DRenderable*>::Pair *pPair = m_MeshHashInstances.GetPair(i); 
		D3DMesh* pMesh = (D3DMesh*)pPair->m_Key;
		GetDevice()->SetTexture(1, pMesh->getTexture(D3DShader::UNIT_B)); 
		GetDevice()->SetTexture(2, pMesh->getTexture(4)); 
		pDevice->SetStreamSourceFreq(0, D3DSTREAMSOURCE_INDEXEDDATA | pPair->m_Count); 

		pDevice->SetStreamSource(1, m_pInstanceVB, uiInstanceBytesOffset, sizeof(D3DShader::InstanceData)); 
		pDevice->SetStreamSourceFreq(1, D3DSTREAMSOURCE_INSTANCEDATA | 1); 
		pDevice->SetIndices(pMesh->pIndexBuffer); 

		unsigned int passes = 0; 
		m_pEffect->Begin(&passes, 0); 
		for(unsigned int p = 0;  p < passes;  p++)
		{
			m_pEffect->BeginPass(p); 
			pMesh->Render(vbBytesOffset);
			m_pEffect->EndPass(); 
		}
		m_pEffect->End(); 

		uiInstanceBytesOffset += (pPair->m_Count * sizeof(D3DShader::InstanceData)); 
		vbBytesOffset += pMesh->getVertexCount();
	}

	//Renders all geometry that is subjected to masking
	const unsigned int maskedMeshCount = m_MaskMeshHashInstances.TableKeyCount(); 
	uiInstanceBytesOffset = 0; 
	m_pEffect->SetTechnique(mp_MaskDepthNormalHandle ); 
	for(unsigned int i = 0;  i < maskedMeshCount;  i++)
	{
		HashTable<InstanceData, D3DRenderable*>::Pair *pPair = m_MaskMeshHashInstances.GetPair(i); 
		D3DMesh* pMesh = (D3DMesh*)pPair->m_Key;  //TODO: not safe

		GetDevice()->SetTexture(0, pMesh->getTexture(D3DShader::UNIT_MASK)); 
		GetDevice()->SetTexture(1, pMesh->getTexture(D3DShader::UNIT_B)); 
		GetDevice()->SetTexture(2, pMesh->getTexture(4)); 

		pDevice->SetStreamSourceFreq(0, D3DSTREAMSOURCE_INDEXEDDATA | pPair->m_Count); 

		pDevice->SetStreamSource(1, mp_MaskInstanceVB, uiInstanceBytesOffset, sizeof(D3DShader::InstanceData)); 
		pDevice->SetStreamSourceFreq(1, D3DSTREAMSOURCE_INSTANCEDATA | 1); 
		pDevice->SetIndices(pMesh->pIndexBuffer); 

		unsigned int passes = 0; 
		m_pEffect->Begin(&passes, 0); 
		for(unsigned int p = 0;  p < passes;  p++)
		{
			m_pEffect->BeginPass(p); 
			pMesh->Render(vbBytesOffset);
			m_pEffect->EndPass(); 
		}
		m_pEffect->End(); 

		uiInstanceBytesOffset += (pPair->m_Count * sizeof(D3DShader::InstanceData)); 
		vbBytesOffset += pMesh->getVertexCount();
	}

	pDevice->EndScene(); 

	//Prevents our previous RT's from being cleared when clearing buffers
	//for lighting computations
	//pDevice->SetRenderTarget(0, NULL); 
	pDevice->SetRenderTarget(1, NULL); 
	pDevice->SetRenderTarget(2, NULL); 
	pDevice->SetRenderTarget(3, NULL); 

	#ifdef _DEBUG
	{
		LPCWSTR pMsg = L"End Normal/Depth\n"; 
		D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); 
	}
#endif
}


/**************************************
 * Executes the shader which usually results
 * in geometry being Drawn
 **************************************/
void D3DLightShader::Execute(const int flags)
{
	unsigned int renderMode = GetDevice()->GetRenderMode();
	if(!(renderMode == CD3D9Device::DEFAULT) && !(renderMode == CD3D9Device::LIGHTING))
		return;

	const ViewOrrientation& viewPerspective = GetDevice()->m_camera;

	D3DXMATRIX invView;
	D3DXMatrixMultiply(&matViewProjection,&viewPerspective.m_View, &viewPerspective.m_Projection); 
	D3DXMatrixInverse(&matInvViewProjection,NULL,&matViewProjection); 
	D3DXMatrixInverse(&invView,NULL,&viewPerspective.m_View); 
	
	
	LPDIRECT3DDEVICE9 const pDevice = GetDevice()->m_pD3DDevice; 

	for(unsigned int numLights = GetDevice()->m_DirectionalLights.Count(), L = 0;  L < numLights;  L++)
		GetDevice()->m_DirectionalLights[L]->Update(); 

	m_pEffect->SetMatrix(m_pProjectionHandle, &viewPerspective.m_Projection); 
	m_pEffect->SetMatrix(m_pViewHandle, &viewPerspective.m_View); 
	m_pEffect->SetMatrix(mp_LastViewHandle, &matLastView);
	matLastView = viewPerspective.m_View;

	m_pEffect->SetMatrix(mp_InvViewProjHandle ,&matInvViewProjection);
	m_pEffect->SetMatrix("g_mInvView",&invView);

	D3DXMATRIX invTranView;
	D3DXMatrixTranspose(&invTranView, &invView);
	m_pEffect->SetMatrix("g_mInvTranView",&invTranView);
	
	GetDevice()->SetCurrentVertexDeclaration(m_pVertexDeclaration); 

	RenderNormalDepthBuffer(); 
	RenderLighting(); 		
}

/**************************************
 * Provides client access to the result
 * of this shader. Shader must be unlocked
 * for data to be retrieved
 **************************************/
LPDIRECT3DTEXTURE9 D3DLightShader::GetLightBuffer()
{
	return mb_Locked ? (LPDIRECT3DTEXTURE9)GetDevice()->GetWhiteTexture()->DataD3D() : pLightTextureA;
}
/**************************************
 * Provides client access to the velocity
 * buffer produced by this shader. Shader 
 * must be unlocked for data to be retrieved
 **************************************/
LPDIRECT3DTEXTURE9 D3DLightShader::GetVelocityBuffer()
{
	return mb_Locked ? (LPDIRECT3DTEXTURE9)GetDevice()->GetWhiteTexture()->DataD3D() : m_RTVelocity->m_pTargetTexture;
}

/**************************************
 * Allows shader implementations to
 * specfiy custom parameters for an effect
 **************************************/
HRESULT D3DLightShader::InitalizeParameters(LPD3DXEFFECT pEffect)
{
	if(ISNULL(pEffect))
		return E_FAIL;

	m_pProjectionHandle = pEffect->GetParameterBySemantic( NULL, "PROJECTION" ); 
	if(ISNULL(m_pProjectionHandle)) throw EE_EXCEPTION("");

	m_pViewHandle = pEffect->GetParameterBySemantic( NULL, "VIEW" ); 
	if(ISNULL(m_pViewHandle)) throw EE_EXCEPTION("");

	mp_LastViewHandle = pEffect->GetParameterBySemantic( NULL, "LASTVIEW" ); 
	if(ISNULL(mp_LastViewHandle)) throw EE_EXCEPTION("");

	mp_LightDataHandle = pEffect->GetParameterBySemantic( NULL, "LIGHTDATA" ); 
	if(ISNULL(mp_LightDataHandle)) throw EE_EXCEPTION("");
	
	mp_InvViewProjHandle = pEffect->GetParameterBySemantic( NULL, "INVVIEWPROJ" ); 
	if(ISNULL(mp_InvViewProjHandle)) throw EE_EXCEPTION("");

	mp_CameraWSHandle = pEffect->GetParameterBySemantic( NULL, "CAMERAWS" ); 
	if(ISNULL(mp_CameraWSHandle)) throw EE_EXCEPTION("");

	m_pInvMapDimensions = pEffect->GetParameterBySemantic(NULL, "INVLIGHTDIMENSIONS"); 
	if(ISNULL(m_pInvMapDimensions)) throw EE_EXCEPTION("");

	mp_DepthNormalHandle = pEffect->GetTechniqueByName("t_ShadeDepthNormal"); 
	if(ISNULL(mp_DepthNormalHandle)) throw EE_EXCEPTION("");

	mp_MaskDepthNormalHandle = pEffect->GetTechniqueByName("t_MaskShadeDepthNormal"); 
	if(ISNULL(mp_MaskDepthNormalHandle)) throw EE_EXCEPTION("");

	mp_ComputeLightingHandle = pEffect->GetTechniqueByName("t_ComputeOmniLighting"); 
	if(ISNULL(mp_ComputeLightingHandle)) throw EE_EXCEPTION("");
	
	mp_ComputeDirLightingHandle = pEffect->GetTechniqueByName("t_ComputeDirectionalLighting"); 
	if(ISNULL(mp_ComputeDirLightingHandle)) throw EE_EXCEPTION("");

	mp_ComputeSunLightingHandle = pEffect->GetTechniqueByName("t_ComputeSunLight"); 
	if(ISNULL(mp_ComputeSunLightingHandle)) throw EE_EXCEPTION("");
	
	return S_OK;
}

/**************************************
 * Attempts to allocate and initalize
 * rendertargets
 **************************************/
void D3DLightShader::CreateRenderTargets()
{	
	HRESULT hr;
	do {

		ReleaseRT(&m_RTDepth);
		ReleaseRT(&m_RTNormal);
		ReleaseRT(&m_RTLightbuffer);
		ReleaseRT(&m_RTLightbuffer);
		ReleaseRT(&m_RTVelocity);
		ReleaseRT(&m_RTStaticLighting);

		m_RTDepth		= EE_NEW RenderTarget(GetDevice()); 
		m_RTNormal		= EE_NEW RenderTarget(GetDevice()); 
		m_RTLightbuffer	= EE_NEW RenderTarget(GetDevice()); 
		m_RTLightbuffer	= EE_NEW RenderTarget(GetDevice()); 
		m_RTVelocity	= EE_NEW RenderTarget(GetDevice()); 
		m_RTStaticLighting = EE_NEW RenderTarget(GetDevice());

		cuint bufferWidth = GetDevice()->m_deviceSettings.screenWidth;
		cuint bufferHeight = GetDevice()->m_deviceSettings.screenHeight;

		if(bufferWidth == 0 || bufferHeight == 0) {
			EE_LOGERROR("Bad device state.");
			hr = E_UNEXPECTED;
			break;
		}

		if(FAILED(GetDevice()->createRenderTarget(m_RTDepth, bufferWidth, bufferHeight, D3DFMT_G16R16, false))) {
			EE_LOGERROR("Could not create depth RT."); 
			hr = E_FAIL;
			break;
		}
		if(FAILED(GetDevice()->createRenderTarget(m_RTNormal, bufferWidth, bufferHeight, D3DFMT_A8R8G8B8, false))) {
			EE_LOGERROR("Could not create normal RT."); 
			hr = E_FAIL;
			break;
		}
		if(FAILED(GetDevice()->createRenderTarget(m_RTVelocity, bufferWidth, bufferHeight, D3DFMT_G16R16, false))) {
			EE_LOGERROR("Could not create velocity RT."); 
			hr = E_FAIL;
			break;
		}
		if(FAILED(GetDevice()->createRenderTarget(m_RTLightbuffer, bufferWidth, bufferHeight, D3DFMT_A8R8G8B8, false))) {
			EE_LOGERROR("Could not create compund RT."); 
			hr = E_FAIL;
			break;
		}
		if(FAILED(GetDevice()->createRenderTarget(m_RTStaticLighting, bufferWidth, bufferHeight, D3DFMT_A8R8G8B8, false))) {
			EE_LOGERROR("Could not create static light RT."); 
			hr = E_FAIL;
			break;
		}
		
		if(!ISNULL(m_pEffect))
			m_pEffect->SetVector(m_pInvMapDimensions, &VectorToD3DVector4(Vector3f(1.0f / bufferWidth, 1.0f / bufferHeight, 0.0f)));
	
	}while(false);

}

/**************************************
 * Provides oppourtunity for shader to 
 * execute code after shading, each frame
 **************************************/
void D3DLightShader::PostExecute()
{
	m_MaskMeshHashInstances.ClearAllocatedSpace(); 
	m_MeshHashInstances.ClearAllocatedSpace(); 

	m_SubmittedVertexCount = 0;
	mb_Locked = false; 
}

/**************************************
 * Enables client specification of ambient
 * light effects
 **************************************/
void D3DLightShader::SetSkyData(const SunData &skyData)
{
	const D3DXVECTOR3* pDirection = &skyData.m_Direction;
	m_skyData.direction[0] = pDirection->x;
	m_skyData.direction[1] = pDirection->y;
	m_skyData.direction[2] = pDirection->z;

	const D3DXVECTOR3* pAmbience = &skyData.m_Ambience;
	m_skyData.ambientColor[0] = pAmbience->x;
	m_skyData.ambientColor[1] = pAmbience->y;
	m_skyData.ambientColor[2] = pAmbience->z;


	const D3DXVECTOR3* pDiffuse = &skyData.m_Diffuse;
	m_skyData.diffuseColor[0] = pDiffuse->x;
	m_skyData.diffuseColor[1] = pDiffuse->y;
	m_skyData.diffuseColor[2] = pDiffuse->z;

	m_skyActive = skyData.m_IsActive;
}

/**************************************
 * Initalizes the resource
 **************************************/
HRESULT D3DLightShader::Create(const string &szFilename)
{
	HRESULT hr;
	LPD3DXBUFFER pErrors	= NULL; 
	DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE; 
#ifdef _DEBUG
	dwShaderFlags |= D3DXSHADER_DEBUG; 
#endif

	do {

		if(ISNULL(GetDevice())) {
			hr = E_UNEXPECTED;
			EE_LOGERROR("Shader has no graphics device."); 
			break;
		}

		if(!szFilename.IsValid()) {
			hr = E_INVALIDARG;
			EE_LOGERROR("Bad shader filename."); 
			break;
		}

		if(ISNULL(m_pEffect)) {
			
			ReleaseResources();
			
			const string filepath = CEngine::GetInstance()->FindFilepath(szFilename); 
			if(!filepath.IsValid()) {
				hr = E_UNEXPECTED;
				EE_LOGERROR("Failed to locate file."); 
				break;
			}

			if(D3DXCreateEffectFromFile(GetDevice()->GetDevice(),filepath.c_str(), NULL, NULL, 
				dwShaderFlags,GetEffectPool() ,&m_pEffect,&pErrors) != D3D_OK) {
					hr = E_FAIL;
					EE_LOGERROR(pErrors ? (char*)pErrors->GetBufferPointer() : "Unknown shader error"); 
					break;
			}
		}

		if(FAILED(D3DXCreateSphere(GetDevice()->GetDevice(), 1.0f, 10, 10, &m_pLightSphere, NULL)) ||
			FAILED(D3DXCreateCylinder(GetDevice()->GetDevice(), 1.0f, 1.0f, 15.0f, 8, 1,	&m_pLightCylinder, NULL))) {
			hr = E_FAIL;
			EE_LOGERROR("Failed to create light geometry.");
			break;
		}

		if(FAILED(InitalizeParameters(m_pEffect))) {
			hr = E_FAIL;
			EE_LOGERROR("Failed to initalize effect parameters."); 
			break;
		}
	
		if(FAILED(GetDevice()->GetDevice()->CreateVertexDeclaration(InstanceDecl, &m_pVertexDeclaration))) {
			hr = E_FAIL;
			EE_LOGERROR("CreateVertexDeclaration(..) failed."); 
			break;
		}

		CreateRenderTargets();
		m_skyActive = true;
		EE_ZEROMEMORY(&m_skyData, sizeof(m_skyData));
		m_skyData.direction[0] = -1.0f;
		m_skyData.direction[1] = -1.0f;
		m_skyData.direction[2] = 1.0f;
		m_skyData.ambientColor[0] = 0.1f;
		m_skyData.ambientColor[1] = 0.1f;
		m_skyData.ambientColor[2] = 0.1f;
		m_skyData.diffuseColor[0] = 0.2f;
		m_skyData.diffuseColor[1] = 0.2f;
		m_skyData.diffuseColor[2] = 0.1f;

		m_Identity = szFilename; 
		return S_OK; 

	}while(false);

	Release(); 
	return hr;
}

/**************************************
 * Enables an effect to be reloaded from
 * file during runtime
 **************************************/
HRESULT	D3DLightShader::Reload(void)
{
	LPD3DXBUFFER pErrors	= NULL; 
	LPD3DXEFFECT pEffect	= NULL;
	DWORD dwShaderFlags		= D3DXFX_NOT_CLONEABLE; 
#ifdef _DEBUG
	dwShaderFlags |= D3DXSHADER_DEBUG; 
#endif

	try {
	
		if(!m_Identity.IsValid() || ISNULL(m_pEffect))
			throw EE_EXCEPTION("Bad shader state.")

		const string filepath = CEngine::GetInstance()->FindFilepath(m_Identity); 
		if(!filepath.IsValid())
			throw EE_EXCEPTION("Failed to locate file."); 

		//Load the shader
		if(D3DXCreateEffectFromFile(GetDevice()->GetDevice(),filepath.c_str(),NULL,NULL,
			dwShaderFlags,GetEffectPool(),&pEffect,&pErrors) != D3D_OK)
			throw EE_EXCEPTION(pErrors ? (char*)pErrors->GetBufferPointer() : "Unknown shader error"); 

		if(FAILED(InitalizeParameters(pEffect)))
		{
			//Fix up parameters for origonal effect
			InitalizeParameters(m_pEffect);
			throw EE_EXCEPTION("Failed to initialize effect parameters during reload.")
		}

		CreateRenderTargets();
		
		//Seems safe to Release origonal effect
		ReleaseResources();
		m_pEffect = pEffect;

		return S_OK; 
	}catch(CError::CErrorData error)
	{
		CError::CError(error);
		if(!ISNULL(pEffect))
		{
			pEffect->Release();
			pEffect = NULL;
		}
		return E_FAIL; 
	}
}

/**************************************
 * Utility helper function
 **************************************/
void D3DLightShader::ReleaseRT(RenderTarget** pRT)
{
	if(pRT && *pRT)
	{
		(*pRT)->Release();
		EE_SAFEDELETE((*pRT));
	}
}

/**************************************
 *Attempts to Release resoruces associated
 *with this shader instance
 **************************************/
void D3DLightShader::ReleaseResources()
{
	if(m_pEffect)
	{
		m_pEffect->Release(); 
		m_pEffect = NULL; 
	}
}

/**************************************
 *Attempts to Release resoruces associated
 *with this shader instance as well as itself
 *from the parent device
 **************************************/
HRESULT D3DLightShader::Release()
{
	m_IsValid = false;
	if(FAILED(GetDevice()->DeregisterResource(this)))
	{
		m_IsValid = true;
		return E_FAIL;
	}

	ReleaseResources();

	if(!ISNULL(m_pVertexDeclaration))
	{
		m_pVertexDeclaration->Release();
		m_pVertexDeclaration = NULL;
	}
	
	if(!ISNULL(m_pInstanceVB))
	{
		m_pInstanceVB->Release();
		m_pInstanceVB = NULL;
	}
	if(!ISNULL(m_pMeshVB))
	{
		m_pMeshVB->Release();
		m_pMeshVB = NULL;
	}

	//Placed here so that RT's are only released
	//when object is removed. Therefore they aren't
	//replaced during runtime reloads
	ReleaseRT(&m_RTDepth);
	ReleaseRT(&m_RTNormal);
	ReleaseRT(&m_RTLightbuffer);
	ReleaseRT(&m_RTLightbuffer);
	ReleaseRT(&m_RTVelocity);
	return S_OK; 
}